﻿using System;
using Sencha.Serialization.Contracts;

namespace Sencha.Serialization.Serializers
{
	public sealed class NullableSerializer<T> : TypeSerializer<T?> where T : struct
	{
		private TypeSerializer<T> innerSerializer;
		private int contractVersion;
		private DataContract contract;

		public NullableSerializer(DataContract contract, TypeDescription type)
		{
			if (contract == null)
				throw new ArgumentNullException("contact");

			this.contract = contract;
		}

		public override T? Deserialize(IJsonReader reader)
		{
			if (reader.Token == JsonToken.Null)
				return default(T);

			this.EnsureInnerSerializer();

			return innerSerializer.Deserialize(reader);

		}
		public override void Serialize(IJsonWriter writer, T? value)
		{
			if (!value.HasValue)
			{
				writer.WriteNull();
				return;
			}

			this.EnsureInnerSerializer();

			innerSerializer.Serialize(writer, value.Value);
		}

		private void EnsureInnerSerializer()
		{
			var cv = contract.Version;
			if (contractVersion == cv && innerSerializer != null)
				return;

			if (System.Threading.Interlocked.CompareExchange(ref this.innerSerializer, contract.GetSerializerOfType<T>(), null) == null)
				System.Threading.Interlocked.Exchange(ref this.contractVersion, cv);
		}
	}
}
